#! -*- coding:utf-8 -*-
import json
import logging
import time
from datetime import datetime, timedelta

from django.core.management.base import BaseCommand

from common.cache import redis_cache
from common.channel.model import Channel
from common.mch.model import MchChannel
from common.order.model import PAY_STATUS
from common.order.model import PayOrder
from common.utils import exceptions as err
from common.utils import telegram
from common.utils import tz
from common.utils.send_p import send_text_msg, ADMIN

_LOGGER = logging.getLogger(__name__)

BLACK_MCHS = [6001016, 6001019]

MCHS = {
    6001000: {
        'name': 'Witch',
        'ts': 900 * 2,
    },
    6001011: {
        'name': 'Durotar',
        'ts': 900 * 2,
    },
    6001015: {
        'name': 'Loki',
        'ts': 1800 * 2,
    },
    6001017: {
        'name': 'zs',
        'ts': 1800 * 10,
    },
    6001022: {
        'name': 'tt',
        'ts': 900 * 2,
    },
}

NOTIFY_UIDS = [
    ADMIN.KAEL,
    ADMIN.MIKE,
    ADMIN.ARNO,
    ADMIN.OWEN,
]
GROUP_ID = 4335


def _check_channel_is_risk(channel_id, chn_info):
    # 检查累积充值风控
    try:
        # _LOGGER.info('check _check_channel_is_risk channel_id:%s, chn_info: %s ', channel_id, chn_info)
        if 'incr_risk' in chn_info:
            daily_stats, hourly_stats, minutely_stats = redis_cache.get_chn_amount(channel_id)
            risk_conf = chn_info['incr_risk']
            if 'daily' in risk_conf:
                daily_conf = risk_conf['daily']
                amount, count = daily_conf.get('amount'), daily_conf.get('count', 1000000)
                if amount and int(daily_stats.get('amount', 0)) >= amount:
                    raise err.ReachLimit('chn reached daily amount limit, %s' % channel_id)
                if count and int(daily_stats.get('count', 0)) >= count:
                    raise err.ReachLimit('chn reached daily count limit, %s' % channel_id)
            if 'hourly' in risk_conf:
                hourly_conf = risk_conf['hourly']
                amount, count = hourly_conf.get('amount'), hourly_conf.get('count', 1000000)
                if amount and int(hourly_stats.get('amount', 0)) >= amount:
                    raise err.ReachLimit('chn reached hourly amount limit, %s' % channel_id)
                if count and int(hourly_stats.get('count', 0)) >= count:
                    raise err.ReachLimit('chn reached hourly count limit, %s' % channel_id)
            if 'minutely' in risk_conf:
                minutely_conf = risk_conf['minutely']
                amount, count = minutely_conf.get('amount'), minutely_conf.get('count', 1000000)
                if amount and int(minutely_stats.get('amount', 0)) >= amount:
                    raise err.ReachLimit('chn reached minutely amount limit, %s' % channel_id)
                if count and int(minutely_stats.get('count', 0)) >= count:
                    raise err.ReachLimit('chn reached minutely count limit, %s' % channel_id)
    except Exception as e:
        _LOGGER.warn('check incr_risk warn, %s', e)
        return True
    return False


def _check_channel_daily_vaild_time(chn_info):
    try:
        if 'daily_open_time' in chn_info:
            daily_open_time = chn_info['daily_open_time']
            start = daily_open_time.get('start', 0)
            end = daily_open_time.get('end', 24)
            if start:  # 定时开启通道 30min内不报告警
                n = tz.local_now()
                today_start = datetime.strptime("{}-{}-{} {}:0:0".format(n.year, n.month, n.day, start),
                                                "%Y-%m-%d %H:%M:%S")
                ln = datetime.strptime("{}-{}-{} {}:{}:{}".format(n.year, n.month, n.day, n.hour, n.minute, n.second),
                                       "%Y-%m-%d %H:%M:%S")
                if ln < today_start + timedelta(minutes=30):
                    return True
            hour = tz.local_now().hour
            if start > end:  # 跨日
                if hour > end:
                    end += 24
                else:
                    end += 24
                    hour += 24
            if hour < start or hour >= end:
                return True
    except Exception as e:
        _LOGGER.exception('_check_channel_daily_vaild_time exceptions, %s', e)
        return False
    return False


_TG_FINANCE_WARNING_GROUP = -1001331494801


# 近120钟成功率低于15%的通道告警
def check_channel_success_rate(channel_id, mch_name, service):
    start = datetime.utcnow() + timedelta(minutes=-120)
    success_count = PayOrder.query.filter(PayOrder.channel_id == channel_id).filter(PayOrder.status == PAY_STATUS.SUCC). \
        filter(PayOrder.created_at >= start).count()
    total_count = PayOrder.query.filter(PayOrder.channel_id == channel_id).filter(PayOrder.created_at >= start).count()
    if total_count > 0:
        rate = float(success_count) / float(total_count)
    else:
        rate = 0
    if total_count > 10 and rate < 0.2 and success_count > 0:
        msg = u'{} {} 支付通道 {} 近两小时,成功率:{}%,成功订单数：{},总订单数：{}'.format(mch_name, service, channel_id,
                                                                    '%.2f' % (rate * 100.0), success_count, total_count)
        telegram.send_text_msg_tele(_TG_FINANCE_WARNING_GROUP, msg)


# 支付告警-支付通道异常
# 商户号:6001015(Loki)
# 通道ID：(772-alipay)
# 通道名称:139013-鑫支付
# 原因：超过710分钟没有成功订单
class Command(BaseCommand):
    def handle(self, **options):
        last_id = redis_cache.get_last_monitor_id()
        print 'last id ', last_id
        orders = PayOrder.query.filter(PayOrder.id > last_id).filter(
            PayOrder.status == PAY_STATUS.SUCC).all()
        for order in orders:
            channel_id = order.channel_id
            redis_cache.set_chn_last_pay(channel_id, order.payed_at)
            redis_cache.set_last_monitor_id(order.id)
        # check channel
        chns = Channel.query.filter(Channel.status == 1).filter(
            Channel.service_name != 'test').all()
        for chn in chns:
            try:
                mch_channels = MchChannel.query.filter(MchChannel.mch_id == chn.mch_id).filter(
                    MchChannel.service_name == chn.service_name).first()
                if mch_channels is None or str(chn.id) not in mch_channels.chns.split(','):
                    continue
                if _check_channel_daily_vaild_time(json.loads(chn.info)):
                    continue
                if _check_channel_is_risk(chn.id, json.loads(chn.info)):
                    continue
                if chn.mch_id in BLACK_MCHS:
                    continue
                last_ts = redis_cache.get_chn_last_pay(chn.id)
                channel_update = int(time.mktime(chn.updated_at.timetuple()))
                if channel_update > last_ts:
                    last_ts = channel_update
                now_ts = int(time.time())
                delta_ts = now_ts - last_ts
                tg_msg = u'支付告警\n'
                if last_ts and delta_ts > MCHS[chn.mch_id]['ts']:
                    print 'chn %s has gone for too much long time' % chn.id
                    _LOGGER.warn('monitor_pay, chn %s is dead, service %s, name %s, mch %s',
                                 chn.id, chn.service_name, chn.name, chn.mch_id)
                    title = u'商户{}支付通道异常'.format(MCHS[chn.mch_id]['name'])
                    body = u'通道{}({}-{})超过{}分钟没有成功订单，商户号{}'.format(
                        chn.name, chn.id, chn.service_name, delta_ts / 60, chn.mch_id)
                    msg = u'**{}**\n**{}**\n**{}**'.format(
                        u'支付告警', title, body)
                    send_text_msg(3, GROUP_ID, msg)
                    tg_msg += title + '\n' + body + '\n'
                if tg_msg != u'支付告警\n':
                    telegram.send_text_msg_tele(_TG_FINANCE_WARNING_GROUP, tg_msg)

                check_channel_success_rate(chn.id, MCHS[chn.mch_id]['name'], chn.service_name)
            except Exception as e:
                _LOGGER.exception("monitor pay error %s" % e)
